#ifndef RTE_KB1285_ESP32_H
#define RTE_KB1285_ESP32_H

#include <logger_rte.h>
#include <lwesp/lwesp_typedefs.h>
#include <lwesp/lwesp.h>
#include <netconn_client.h>
#include <station_manager.h>
#include <utils.h>
#include "kSerial.h"

class esp32_server : public Stream {
public:
    int begin();

    size_t write(uint8_t) override;

    int available() override;

    int read() override;

    int peek() override;

    static void the_server(const void *arg) {
        auto cls = (esp32_server *) arg;
        cls->loop();
    }

    void loop();
};

class ble_server : public Stream {

};

/**
 * \brief           Private access-point and station management system
 *
 * This is used for asynchronous connection to access point
 */
typedef struct {
    size_t index_preferred_list;                /*!< Current index position of preferred array */
    size_t index_scanned_list;                  /*!< Current index position in array of scanned APs */

    uint8_t command_is_running;                 /*!< Indicating if command is currently in progress */
} prv_ap_data_t;

/* Arguments for callback function */
#define ARG_SCAN                (void*)1
#define ARG_CONNECT             (void*)2

/* Function declaration */
static void prv_cmd_event_fn(lwespr_t status, void *arg);

static void prv_try_next_access_point();

class esp32 {
public:
    int begin(kSerial *);

    int ble_start();

    [[noreturn]] static void thd_esp32(const void *);

    kSerial *serial;
    size_t ap_list_scanned_len = 0;          /* Number of scanned access points */
    prv_ap_data_t ap_async_data;             /* Asynchronous data structure */

    /* Command to execute to start scanning access points */
#define prv_scan_ap_command_ex(blocking)        lwesp_sta_list_ap(NULL, ap_list_scanned, LWESP_ARRAYSIZE(ap_list_scanned), &ap_list_scanned_len, NULL, NULL, (blocking))
#define prv_scan_ap_command()                   do {\
if (!ap_async_data.command_is_running) {    \
printf("Starting scan command on line %d\r\n", __LINE__);\
ap_async_data.command_is_running = lwesp_sta_list_ap(NULL, ap_list_scanned, LWESP_ARRAYSIZE(ap_list_scanned), &ap_list_scanned_len, prv_cmd_event_fn, ARG_SCAN, 0) == lwespOK;   \
}       \
} while (0)

    /**
 * \brief           Every internal command execution callback
 * \param[in]       status: Execution status result
 * \param[in]       arg: Custom user argument
 */
    static void prv_cmd_event_fn(lwespr_t status, void *arg);

    /**
     * \brief           Try to connect to next access point on a list
     */
    void prv_try_next_access_point();

    /**
     * \brief           Private event function for asynchronous scanning
     * \param[in]       evt: Event information
     * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
     */
    static lwespr_t prv_evt_fn(lwesp_evt_t *evt);

    /**
     * \brief           Initialize asynchronous mode to connect to preferred access point
     *
     * Asynchronous mode relies on system events received by the application,
     * to determine current device status if station is being, or not, connected to access point.
     *
     * When used, async acts only upon station connection change through callbacks,
     * therefore it does not require additional system thread or user code,
     * to be able to properly handle preferred access points.
     * This certainly decreases memory consumption of the complete system.
     *
     * \ref LWESP_CFG_KEEP_ALIVE feature must be enable to properly handle all events
     * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
     */
    lwespr_t station_manager_connect_to_access_point_async_init();

    /**
     * \brief           Connect to preferred access point in blocking mode
     *
     * This functionality can only be used if non-blocking approach is not used
     *
     * \note            List of access points should be set by user in \ref ap_list structure
     * \param[in]       unlimited: When set to 1, function will block until SSID is found and connected
     * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
     */
    lwespr_t connect_to_preferred_access_point(uint8_t unlimited);

    ap_entry_t ap_list_preferred[10] = {
            //{ .ssid = "SSID name", .pass = "SSID password" },
            {.ssid = "TilenM_ST", .pass = "its private"},
            {.ssid = "Kaja", .pass = "kajagin2021"},
            {.ssid = "Majerle WIFI", .pass = "majerle_internet_private"},
            {.ssid = "Majerle AMIS", .pass = "majerle_internet_private"},
    };
    lwesp_ap_t ap_list_scanned[10];         /* Scanned access points information */
    void load();

    int sleep(int level, uint32_t delay);
};

void wifi_reload();

#endif //RTE_KB1285_ESP32_H
